package net.obxlabs.tweetpad;

import com.knowledgebooks.nlp.util.*;
import java.awt.*;
import java.util.*;
import net.nexttext.*;
import net.nexttext.behaviour.*;
import net.nexttext.property.*;
import net.silentlycrashing.dictionary.*;

/**
 * A TextObject builder for tweetPad.
 */
/* $Id: Maker.java 24 2008-11-21 03:51:01Z prisonerjohn $ */
public class Maker extends TextObjectBuilder {
	// limit for Latin characters in Unicode
	public static final char MAX_LATIN = 0x017F;
	
	private static FastTagger fastTagger;
	private static Thesaurus thesaurus;
	
	static {
		fastTagger = new FastTagger();
		thesaurus = new Thesaurus();
	}

    //////////////////////////////////////////////////////////////////////
    // Constructors
    
    /**
     * Instantiates the Maker.
     * 
     * @param book the Book to build TextObjects into
     */
    public Maker(Book book) {
    	this(book, (TextPage)book.getPage("Default Text Page"));
    }
    
    /**
     * Instantiates the Maker.
     * 
     * @param book the Book
     * @param page the Page to build TextObjects into
     */
    public Maker(Book book, TextPage page) {
    	super(book, page);
    }
    
    /**
     * Build a tree of TextObjects from the given string.
     * <P>linelength has no purpose but is included to differentiate between the buildSentence() methods in the superclass.</P>
     */
    public TextObjectGroup buildSentence(String text, Vector3 pos, int lineLength) {
    	// Pre-process the text
        //text = preprocessMessage(text, lineLength);
    	
    	// check if the string is all latin or not
    	boolean isLatin = true;
    	for (int i=0; i < text.length(); i++) {
    		if (text.charAt(i) > MAX_LATIN) {
    			isLatin = false;
    			break;
    		}
    	}
        
    	String[] words;
    	int[] tags;
    	if (isLatin) {
    		words = Tokenizer.wordsToArray(text.toString());
    		tags = fastTagger.tagWords(words);
    	} else {
    		words = new String[1];
    		words[0] = text.toString();
    		tags = new int[1];
    		tags[0] = Meaning.UNKNOWN;
    	}
        
        TextObjectGroup parentGroup = new TextObjectGroup(pos);
        TextObjectGroup tokenGroup;
        Vector3 gOffset = new Vector3(0, 0, 0);
        int wordIndex = 0;
        String currWord;
        int stringIndex = 0;
        String currSubstring;
        int locCount = 1;
        
        while (wordIndex < tags.length) {
        	currWord = words[wordIndex];
        	currSubstring = text.substring(stringIndex, stringIndex+currWord.length());
        	
        	if (currWord.compareTo(currSubstring) == 0) {
        		// we have a match
        		// create the TextObjectGroup
        		//System.out.println(" > Creating smart group '"+currWord+"'");
        		tokenGroup = createGroup(currWord, gOffset);
        		// tag it
        		tokenGroup.init("FastTag", new NumberProperty(tags[wordIndex]));
        		// add it to the parent TextObjectGroup
        		parentGroup.attachChild(tokenGroup);
        		// adjust the variables for the next round
        		gOffset.add(new Vector3(tokenGroup.getBoundingPolygon().getBounds().width+trackingOffset.x, 0, 0));
        		wordIndex++;
        		stringIndex += currWord.length();
        	} else {
        		// no match, probably a space/quote/double-quote
        		// create a TextObjectGroup for the next character
        		currSubstring = text.substring(stringIndex, stringIndex+1);
        		// create the TextObjectGroup
        		//System.out.println(" > Creating dumb group '"+currSubstring+"'");
        		tokenGroup = createGroup(currSubstring, gOffset);
        		// tag it
        		tokenGroup.init("FastTag", new NumberProperty(Meaning.UNKNOWN));
        		// add it to the parent TextObjectGroup
        		parentGroup.attachChild(tokenGroup);
        		// adjust the variables for the next round
        		gOffset.add(new Vector3(tokenGroup.getBoundingPolygon().getBounds().width+trackingOffset.x, 0, 0));
        		stringIndex += currSubstring.length();
        	}
        	
        	// init Sketchpad properties
        	tokenGroup.init("OriginalWord", new StringProperty(currWord));
        	tokenGroup.init("OriginalLocation", new NumberProperty(locCount));
        	tokenGroup.init("ShuffleLocation", new NumberProperty(locCount));
    		tokenGroup.init("Mass", new NumberProperty(text.length()));
    		
        	book.addObject(tokenGroup);
        	
        	locCount++;
        	//System.out.println(" > Word is now '"+parentGroup+"'");
        }
        
        applyBuilderOptions(parentGroup);   
        
        return parentGroup;
    }
    
    /**
     * Applies the builder's parameters. 
     */
    protected void applyBuilderOptions( TextObjectGroup newGroup ) {
        
        if (centerGroup) {
            // Move children of the group to make the position its center,
            // by adding the offset from the center of the bounding box to
            // the group position, to the position of each glyph.
            Rectangle bb = newGroup.getBoundingPolygon().getBounds();
            Vector3 offset = newGroup.getPositionAbsolute();
            offset.sub(new Vector3(bb.getCenterX(), bb.getCenterY()));
            TextObject child = newGroup.getLeftMostChild();
            while (child != null) {
                child.getPosition().add(offset);
                child = child.getRightSibling();
            }
        }

        synchronized (book) {
            if (parent != null) {
                parent.attachChild(newGroup);
            }
            if (addToSpatialList) {
                book.getSpatialList().add(newGroup);
            }

            // Behaviours are added after the data structure is created, so
            // they can be synchronized together, and in case they care about
            // the structure.
            //applyBehaviours(newGroup);
            
        }
    }
    
    /**
     * This methods returns a TextObject group created using the given string,
     * positioned using the given Vector3. Each glyph is contained in its own group, 
     * which is contained in the top level group.
     */
    public TextObjectGroup createGroup(String text, Vector3 pos) {
        TextObjectGroup wordGroup = new TextObjectGroup(groupProperties, pos);

        // Each glyph is offset by gOffset from the word location.
        Vector3 gOffset = new Vector3(0,0,0);
        for (int i = 0; i < text.length(); i++) {
        	TextObjectGroup glyphGroup = new TextObjectGroup(groupProperties, gOffset);
        	glyphGroup.init("OriginalLocation", new NumberProperty(i+1));
        	glyphGroup.init("ShuffleLocation", new NumberProperty(i+1));
        	glyphGroup.init("Mass", new NumberProperty(1));
            String glyph = text.substring(i, i+1);
            TextObjectGlyph to = new TextObjectGlyph(glyph, font, glyphProperties, new Vector3());
			gOffset.x += to.getLogicalBounds().getWidth()+trackingOffset.x;
			to.setDeformed(false);
			glyphGroup.attachChild(to);
            wordGroup.attachChild(glyphGroup);
            book.addObject(to);
            book.addObject(glyphGroup);
        }

        return wordGroup;
    }
    
    public void applyBehaviours(TextObjectGroup newGroup) {
    	// dummy function used so that the parent version isn't called
    }
    
    public void applyBehaviours(TextObject to) {
    	if (to instanceof TextObjectGroup) {
    		applyGroupBehaviours((TextObjectGroup)to);
    	} else if (to instanceof TextObjectGlyph) {
    		applyGlyphBehaviours((TextObjectGlyph)to);
    	}
    }
    
    public void applyGroupBehaviours(TextObjectGroup newGroup) {
    	synchronized (groupBehaviours) {
    		Iterator i = groupBehaviours.iterator();
        	while (i.hasNext()) {
            	((AbstractBehaviour)i.next()).addObject(newGroup);
        	}
    	}
    }

    public void applyGlyphBehaviours(TextObjectGlyph newGlyph) {
    	synchronized (glyphBehaviours) {
    		Iterator i = glyphBehaviours.iterator();
    		while (i.hasNext()) {
    			((AbstractBehaviour)i.next()).addObject(newGlyph);
    		}
    	}
    }
    
    public TextObjectGroup buildSynonym(TextObjectGroup tog) {
    	// get the tag
    	int tag;
    	try {
    		tag = (int)((NumberProperty)tog.getProperty("FastTag")).get();
    	} catch (NullPointerException e) {
    		System.out.println("no tag found for '"+tog+"'!");
    		tag = Meaning.UNKNOWN;
    	}
    	
    	// get the word
    	String word = tog.getString();
    	String synonym;
    	
    	// look for a synonym
    	try {
    		if (tag == Meaning.UNKNOWN) {
    			// get anything
    			synonym = thesaurus.getWord(word).getAnyMeaning().getSomeTerm().getValue();
    		} else {
    			// get a synonym of a specific type
    			synonym = thesaurus.getWord(word).getDumbSynonym(tag);
    		}
    	} catch (WordNotFoundException e) {
    		// no synonym found, don't do anything
    		System.out.println("no synonym found for '"+word+"'");
    		return null;
    	}
    	
    	// build the new TextObjectGroup
    	TextObjectGroup synGroup = createGroup(synonym, tog.getLocation());
    	// set the location
    	synGroup.getPosition().set(tog.getPosition().get());
    	// add the behaviours
    	book.addObject(synGroup);
    	// tag it
    	synGroup.init("FastTag", new NumberProperty(tag));
    	// get the original string
    	try {
    		String originalWord = ((StringProperty)tog.getProperty("OriginalWord")).get();
    		synGroup.init("OriginalWord", new StringProperty(originalWord));
    	} catch (NullPointerException e) {}
    	// set the original location
    	try {
    		int originalLocation = (int)((NumberProperty)tog.getProperty("OriginalLocation")).get();
    		synGroup.init("OriginalLocation", new NumberProperty(originalLocation));
    	} catch (NullPointerException e) {}
    	// set the shuffle location
    	try {
    		int shuffleLocation = (int)((NumberProperty)tog.getProperty("ShuffleLocation")).get();
    		synGroup.init("ShuffleLocation", new NumberProperty(shuffleLocation));
    	} catch (NullPointerException e) {}
    	
    	return synGroup;
    }
    
    public TextObjectGroup buildOriginal(TextObjectGroup tog) {
    	// get the original string
    	String originalWord;
    	try {
    		originalWord = ((StringProperty)tog.getProperty("OriginalWord")).get();
    	} catch (NullPointerException e) {
    		System.out.println("no original found for '"+tog+"'!");
    		return null;
    	}
    	
    	// make sure the words are not identical
    	if (tog.toString().compareTo(originalWord) == 0) {
    		return null;
    	}
    	
    	// build the new TextObjectGroup
    	TextObjectGroup originalGroup = createGroup(originalWord, tog.getLocation());
    	// set the location
    	originalGroup.getPosition().set(tog.getPosition().get());
    	// add the behaviours
    	book.addObject(originalGroup);
    	// tag it
    	int tag;
    	try {
    		tag = (int)((NumberProperty)tog.getProperty("FastTag")).get();
    	} catch (NullPointerException e) {
    		tag = Meaning.UNKNOWN;
    	}
    	originalGroup.init("FastTag", new NumberProperty(tag));
    	originalGroup.init("OriginalWord", new StringProperty(originalWord));
    	// set the original location
    	try {
    		int originalLocation = (int)((NumberProperty)tog.getProperty("OriginalLocation")).get();
    		originalGroup.init("OriginalLocation", new NumberProperty(originalLocation));
    	} catch (NullPointerException e) {}
    	// set the shuffle location
    	try {
    		int shuffleLocation = (int)((NumberProperty)tog.getProperty("ShuffleLocation")).get();
    		originalGroup.init("ShuffleLocation", new NumberProperty(shuffleLocation));
    	} catch (NullPointerException e) {}
		
    	return originalGroup;
    }
}
